import base64
import datetime
import json
from io import BytesIO
import os
from PIL import Image, ImageDraw, ImageFont
import random
import re
import uuid

from calendar import monthrange
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
from django.contrib.auth.hashers import make_password
from django.core.cache import cache
from django.db.models import Q
from django.http import HttpResponse
from rest_framework import viewsets, mixins
from rest_framework.decorators import action, api_view
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response

from cost.models import *
from cost.re_helper import ACCESSKEYID, ACCESSSECRET, SMS_CODE, SIGN_NAME, TEL, PASSWORD
from cost.serializers import UserSerializer, AccountSerializer, DetailSerializer, UserDetailSerializer, \
    AccountDetailSerializer, UserRegisterSerializer, UserLoginSerializer, UpdatePasswordSerializer, \
    UpdateNameSerializer, UpdateTelSerializer, ChangeAccountSerializer, ChangeDetailSerializer

from cost.filters import UserFilter, AccountFilter, DetailFilter, CustomPagePagination, allDays

# Create your views here.
from cost.tests import CITY_DICT


LOCAL_DICT = {'收入': 'local_income', '借出': 'local_jie', '餐饮': 'local_food', '通讯': 'local_phone', '娱乐': 'local_game', '购物': 'local_shop', '交通': 'local_traffic', '杂项': 'local_other'}


# 对用户增删改查的类
from pill_project import settings
from utils.errors import ParamsException


class UserView(viewsets.GenericViewSet,
               mixins.ListModelMixin,  # GET
               mixins.RetrieveModelMixin,
               mixins.DestroyModelMixin,
               mixins.UpdateModelMixin):  #
    # 返回数据，获取所有的用户内容
    # 下面定义的queryset会被ListModelMixin中的self.get_queryset()调用
    """
    ListModelMixin的工作原理就是将queryset取到的model对象丢给filter_queryset过滤,
    然后get_serializer会去调用我们定义的序列化器对象，也就是下文的serializer_class，
    传入过滤后的queryset结果和many=True，返回对象.data,就将对象转换为字典了。
    """
    # queryset = User.objects.all().filter(user_status=1)
    queryset = User.objects.all()
    # 序列化器，序列化结果,就是将对象转换为字典(json)以及字段校验的功能
    serializer_class = UserSerializer


    # parser_classes = (CustomPagePagination,)

    # 重构list方法,只有管理员才准进入,查看所有用户
    def list(self, request, *args, **kwargs):
        # 默认self.get_queryset()方法默认返回上文queryset的结果。
        queryset = self.get_queryset()
        # 过滤器
        queryset = self.filter_queryset(queryset)

        # self.get_serializer()默认返回上文UserSerializer，也就是创建了一个类
        # 实现将queryset中的每个对象转换为字典，many表示序列化对象有多个，一定要写
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    # 重构RetrieveModelMixin,用id查找单一对象,(管理员才能操作)
    def retrieve(self, request, *args, **kwargs):
        # kwargs中的pk为默认的属性名，也就是路由后的/的内容
        # 如 127。0.0.1:8000/cost/1/ 下面则会获取到1对应的id对象
        instance = User.objects.filter(pk=kwargs['pk']).first()
        token = request.query_params.get('token')
        user = User.objects.get(pk=cache.get(token))
        instance = self.get_serializer(instance)
        return Response(instance.data)

    # 重构删除
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.user_status = 0
        instance.save()
        return Response({'code': 200, 'message': '注销成功', 'data': {'username': instance.username}})

    # 重构修改信息
    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        # 传入修改的对象和修改的数据传入serializer进行字段校验和序列化
        serializer = self.get_serializer(instance, data=request.data)
        # raise_exception=True表示校验有失败，直接往外抛异常
        serializer.is_valid(raise_exception=True)
        # 数据保存
        serializer.save()
        return Response(serializer.data)

    # 过滤类，想要起作用，必须在setting中配置过滤
    filter_class = UserFilter

    # 根据用户取得银行卡信息(管理员使用)
    @action(detail=False, methods=['GET'])
    def account(self, request):
        # 请求方式 GET
        # 请求地址 /cost/user/account/
        queryset = self.get_queryset()
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    # 根据用户id取得银行卡信息(管理员使用)
    @action(detail=True, methods=['GET'])
    def accounts(self, request, pk):
        # 请求地址 GET
        # 请求地址 /cost/user/[id]/accounts/
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

    # 根据用户取得消费信息
    @action(detail=False, methods=['GET'])
    def record(self, request):
        # 请求方式 GET
        # 请求地址 /cost/user/record/
        queryset = self.get_queryset()
        # 新定义的序列化器UserDetailSerializer
        serializer = UserDetailSerializer(queryset, many=True)
        return Response(serializer.data)

    # 根据用户id取得消费信息
    @action(detail=True, methods=['GET'])
    def records(self, request, pk):
        # 请求地址 GET
        # 请求地址 /cost/user/[id]/details/
        instance = self.get_object()
        serializer = UserDetailSerializer(instance)
        return Response(serializer.data)

    # 用户登录接口
    @action(detail=False, methods=['POST'], serializer_class=UserLoginSerializer)
    def find_my_password(self, request):
        # 获取参数
        data = request.data
        # 字段校验
        tel = data.get('tel', '1')
        user = User.objects.filter(tel=tel).first()
        password = data.get('password', '1')
        tel_code = data.get('tel_code', '1')
        if not re.fullmatch(TEL, tel):
            raise ParamsException({'code': '8002', 'message': '手机号错误', 'data': {'rule': '手机号错误'}})
        if not user:
            raise ParamsException({'code': '8003', 'message': '该手机号尚未注册', 'data': {'rule': '该手机号尚未注册'}})
        if not re.fullmatch(PASSWORD, password):
            raise ParamsException({'code': '8004', 'message': '密码格式错误', 'data': {'rule': '密码格式错误'}})
        if cache.get(tel) != tel_code:
            raise ParamsException({'code': '8005', 'message': '验证码错误', 'data': {'rule': '验证码错误'}})
        user.password = make_password(password)
        user.save()

        return Response({'message': '修改成功'})


    # 用户登录接口
    @action(detail=False, methods=['POST'], serializer_class=UserLoginSerializer)
    def login(self, request):
        # 获取参数
        data = request.data
        # 字段校验
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        # 获取验证码
        tel_code = data.get('tel_code').lower()
        tel = serializer.validated_data['tel'][-4:]
        # if tel_code != cache.get(tel):
        #     raise ParamsException({'code': '8001', 'message': '验证码错误', 'data': {'rule': '验证码错误'}})
        # 用户登录成功之后，token和登录用户的关联关系保存。
        token = uuid.uuid4().hex
        user = User.objects.get(tel=data.get('tel'))
        user.login_time = datetime.datetime.now()
        user.save()
        # 保存至mysql ，redis(以string类型来存储) ， mongodb
        cache.set(token, user.user_id, timeout=360000)
        return Response({'message': '登录成功', 'token': token, 'username': user.username, 'img_load': user.user_img})

    # 用户注册接口
    @action(detail=False, methods=['POST'], serializer_class=UserRegisterSerializer)
    def register(self, request):
        # 获取参数
        data = request.data
        # 获取前端验证码
        tel_code = data.get('tel_code')

        # 将字段丢给我们定义的序列化器进行字段校验
        serializer = self.get_serializer(data=data)
        # 判断是否验证通过，通过才会进行save，否则会直接弹出错误
        serializer.is_valid(raise_exception=True)
        # 判断验证码是否正确
        # print(cache.get(serializer.validated_data['tel']))
        if cache.get(serializer.validated_data['tel']) != tel_code:
            raise ParamsException({'code': '8001', 'message': '验证码错误', 'data': {'rule': '验证码错误'}})
        # 保存之前对数据进行修改
        serializer.save()
        # print(serializer.data)
        return Response({'code': 200, 'message': '注册成功', 'data': serializer.data})

    # 用户退出登录接口
    @action(detail=False, methods=['GET', 'POST'])
    def logout(self, request):
        token = request.query_params.get('token')
        if not token:
            raise ParamsException({'code': '3001', 'message': '参数错误', 'data': {'rule': '请正确请求'}})
        cache.delete_pattern(token)
        return Response({'code': '200', 'message': '退出登录成功'})

    # 用户修改信息接口
    @action(detail=False, methods=['POST'], serializer_class=UpdatePasswordSerializer)
    def update_password(self, request):
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        new_password = make_password(serializer.validated_data['password1'])
        user = User.objects.get(pk=int(cache.get(serializer.validated_data['token'])))
        user.password = new_password
        user.save()
        return Response({'code': 200, 'message': '修改成功', 'data': serializer.data})

    # 用户修改信息接口
    @action(detail=False, methods=['POST'], serializer_class=UpdateNameSerializer)
    def update_username(self, request):
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        username = serializer.validated_data['username']
        user = User.objects.get(pk=int(cache.get(serializer.validated_data['token'])))
        user.username = username
        user.save()
        return Response({'code': 200, 'message': '修改成功', 'data': serializer.data})

    # 用户修改信息接口 ,post请求 需要token
    @action(detail=False, methods=['POST'], serializer_class=UpdateTelSerializer)
    def update_tel(self, request):
        """
        用于更新用户的手机号，需要验证验证码
        :param request: 网络请求的数据
        :return: 返回处理后的信息
        """
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        tel = serializer.validated_data['tel']
        user = User.objects.get(pk=int(cache.get(serializer.validated_data['token'])))
        # 接受验证码
        # 获取前端验证码
        tel_code = data.get('tel_code')
        if cache.get(serializer.validated_data['tel']) != tel_code:
            raise ParamsException({'code': '8001', 'message': '验证码错误', 'data': {'rule': '验证码错误'}})
        user.save()
        return Response({'code': 200, 'message': '修改成功', 'data': serializer.data})

    # 用户上传image接口
    @action(detail=False, methods=['POST'])
    def update_img(self, request):
        file_data = request.data.get('file')
        file = base64.b64decode(file_data)
        user_token = request.data.get('token')
        if not user_token:
            raise ParamsException({'code': '3001', 'message': '参数错误', 'data': {'rule': '请正确请求'}})
        if not file:
            raise ParamsException({'code': '5002', 'message': '参数错误', 'data': {'rule': '上传为空'}})
        # 自定义头像名字，避免冲突

        file_name = 'png'
        # print(file_name)
        # if file_name not in ['png', 'jpeg', 'jpg']:
        #     raise ParamsException({'code': '5003', 'message': '参数错误', 'data': {'rule': '图片名称错误'}})
        file_name = datetime.datetime.now().strftime('%Y%m%d%H%M%S%f') + '.' + file_name
        user = User.objects.filter(pk=cache.get(user_token)).first()
        # 拼接存储路径
        image_path = os.path.join(settings.UPLOADFILES_DIRS, file_name)
        # 判断用户原有头像是否为默认头像
        old_image = user.user_img
        if old_image == 'none.png':
            user.user_img = file_name
            user.save()
        else:
            old_image_path = os.path.join(settings.UPLOADFILES_DIRS, old_image)
            os.remove(old_image_path)
            user.user_img = file_name
            user.save()
        # content = file.read()
        with open(image_path, 'wb') as f:
            f.write(file)
        return Response({'code': '6001', 'message': '上传成功', 'data': {'img_load': file_name}})

    # 用户查看自己的信息的接口(提供登录的token，根据token查询信息)
    @action(detail=False, methods=['POST'])
    def show_my_info(self, request):
        token = request.data.get('token')
        instance = User.objects.get(pk=cache.get(token))
        instance = self.get_serializer(instance)
        # instance.is_valid(raise_exception=True)
        return Response(instance.data)

    # 用户查询自己的银行卡信息(提供token，根据token查询信息)
    @action(detail=False, methods=['GET', 'POST'], serializer_class=AccountSerializer)
    def show_my_account(self, request):
        token = request.data.get('token')
        instance = User.objects.get(pk=cache.get(token))
        # 取得银行卡对象
        instance = instance.account_set.all()
        instance = self.get_serializer(instance, many=True)
        # instance.is_valid(raise_exception=True)
        return Response(instance.data)

    @action(detail=False, methods=['GET', 'POST'], serializer_class=DetailSerializer)
    def show_my_detail(self, request):
        token = request.data.get('token')
        # 根据token取得用户对象
        instance = User.objects.get(pk=cache.get(token))
        # 取得银行卡对象
        instance = instance.account_set.all()
        ids = [item.account_id for item in instance]
        # print(ids)
        instance = Details.objects.filter(account__in=ids).order_by('-detail_time').all()
        # 创建一个分页对象，此对象为自己编写的分页类，或者也可以是默认的
        paginator = CustomPagePagination()
        # 传入已有的记录
        instance = paginator.paginate_queryset(instance, request)
        # 给序列化器序列化字段
        serializer = self.get_serializer(instance, many=True)
        # 序列化后的结果交给分页
        data = paginator.get_paginated_response(serializer.data)
        # 最后传入前端，可用data.data取得数据

        # instance.is_valid(raise_exception=True)
        return Response(data.data)

    # 根据类型查看
    @action(detail=False, methods=['GET', 'POST'], serializer_class=DetailSerializer)
    def show_my_detail_type(self, request):
        token = request.data.get('token')
        types = request.data.get('type')
        times = request.data.get('times')
        if not(2024 >= int(times.split('-')[0]) >= 2019) or not(12 >= int(times.split('-')[1]) >= 1):
            res = {'code': '3012', 'message': '时间格式错误或超出范围(2019-2023)', 'data': {'rule': 'xxxx-xx'}}
            raise ParamsException(res)
        day = 31
        start_date = datetime.date(int(times.split('-')[0]), int(times.split('-')[1]), 1)
        while True:
            try:
                end_date = datetime.date(int(times.split('-')[0]), int(times.split('-')[1]), day)
                break
            except:
                day = day - 1
                continue

        if types not in ['收入', '借出', '餐饮', '通讯', '娱乐', '购物', '交通', '杂项', '全部']:
            res = {'code': '3002', 'message': '%s交易类型错误请求' % types, 'data': {'rule': types}}
            raise ParamsException(res)
        # 根据token取得用户对象
        instance = User.objects.get(pk=cache.get(token))
        # 取得银行卡对象
        instance = instance.account_set.all()
        ids = [item.account_id for item in instance]
        # print(ids)
        if types == '全部':
            instance = Details.objects.filter(Q(account__in=ids) & Q(detail_time__range=(start_date, end_date))).order_by('-detail_time').all()
        else:
            instance = Details.objects.filter(Q(account__in=ids) & Q(detail_type=types) & Q(detail_time__range=(start_date, end_date))).order_by('-detail_time').all()

        # 创建一个分页对象，此对象为自己编写的分页类，或者也可以是默认的
        paginator = CustomPagePagination()
        # 传入已有的记录
        instance = paginator.paginate_queryset(instance, request)
        # 交给序列化器
        instance = self.get_serializer(instance, many=True)
        data = paginator.get_paginated_response(instance.data)
        # instance.is_valid(raise_exception=True)
        return Response(data.data)

    # 用户查找查看
    @action(detail=False, methods=['GET', 'POST'], serializer_class=DetailSerializer)
    def search_detail(self, request):
        token = request.data.get('token')
        types = request.data.get('type')
        times = request.data.get('times')
        info = request.data.get('info')
        # 根据token取得用户对象
        instance = User.objects.get(pk=cache.get(token))
        # 取得银行卡对象
        instance = instance.account_set.all()
        ids = [item.account_id for item in instance]
        try:
            # 如果查找时间
            if times:
                if not (2024 >= int(times.split('-')[0]) >= 2019) or not (12 >= int(times.split('-')[1]) >= 1):
                    res = {'code': '3012', 'message': '时间格式错误或超出范围(2019-2023)', 'data': {'rule': 'xxxx-xx'}}
                    raise ParamsException(res)
                day = 31
                start_date = datetime.date(int(times.split('-')[0]), int(times.split('-')[1]), 1)
                # 算出结束时间
                while True:
                    try:
                        end_date = datetime.date(int(times.split('-')[0]), int(times.split('-')[1]), day)
                        break
                    except:
                        day = day - 1
                        continue
                instance = Details.objects.filter(Q(account__in=ids) & Q(detail_time__range=(start_date, end_date))).order_by('-detail_time').all()


            elif types:
                if types not in ['收入', '借出', '餐饮', '通讯', '娱乐', '购物', '交通', '杂项', '全部']:
                    res = {'code': '3002', 'message': '%s交易类型错误请求' % types, 'data': {'rule': types}}
                    raise ParamsException(res)

                # print(ids)
                if types == '全部':
                    instance = Details.objects.filter(Q(account__in=ids)).order_by('-detail_time').all()
                else:
                    instance = Details.objects.filter(Q(account__in=ids) & Q(detail_type=types)).order_by('-detail_time').all()
            elif info:
                instance = Details.objects.filter(Q(account__in=ids) & Q(detail_info__contains=info)).order_by('-detail_time').all()
        except:
            res = {'code': '7001', 'message': '查询失败', 'data': {'rule': '类型仅限8种类型或者全部，时间格式为xxxx-xx，范围为2019-1到 2024-12'}}
            raise ParamsException(res)
        # 创建一个分页对象，此对象为自己编写的分页类，或者也可以是默认的
        paginator = CustomPagePagination()
        # 传入已有的记录
        instance = paginator.paginate_queryset(instance, request)
        # 交给序列化器
        instance = self.get_serializer(instance, many=True)
        # instance.is_valid(raise_exception=True)
        data = paginator.get_paginated_response(instance.data)
        # instance.is_valid(raise_exception=True)
        return Response(data.data)


# 银行卡类
class AccountView(viewsets.GenericViewSet,
                  mixins.CreateModelMixin,
                  mixins.DestroyModelMixin,
                  mixins.ListModelMixin,
                  mixins.RetrieveModelMixin,
                  mixins.UpdateModelMixin):

    queryset = Account.objects.all()

    serializer_class = AccountSerializer

    # 重构删除银行卡的方法
    def destroy(self, request, *args, **kwargs):
        instance = Account.objects.filter(pk=kwargs['pk']).first()
        instance.account_status = 0
        instance.save()
        return Response({'message': '删除成功'})

    filter_class = AccountFilter

    # 重构新增银行卡方法
    def create(self, request, *args, **kwargs):
        # 获取参数
        data = request.data
        # 将字段丢给我们定义的序列化器进行字段校验
        serializer = self.get_serializer(data=data)
        # 判断是否验证通过，通过才会进行save，否则会直接弹出错误
        serializer.is_valid(raise_exception=True)
        # 保存之前对数据进行修改
        serializer.save()
        # print(serializer.data)
        return Response({'code': 200, 'message': '新增成功', 'data': serializer.data})

    # 重构新增银行卡方法
    @action(detail=False, methods=['POST'], serializer_class=ChangeAccountSerializer)
    def change_account(self, request):
        # 获取参数
        data = request.data
        # 获取account id
        account_id = data.get('account_id')
        instance = Account.objects.get(pk=account_id)
        if not instance:
            res = {'code': '8002', 'message': '错误请求', 'data': {'rule': '错误请求'}}
            raise ParamsException(res)

        # 将字段丢给我们定义的序列化器进行字段校验
        serializer = self.get_serializer(data=data)
        # 判断是否验证通过，通过才会进行save，否则会直接弹出错误
        serializer.is_valid(raise_exception=True)
        # 修改信息
        instance.account_name = serializer.validated_data['account_name']
        instance.account_num = serializer.validated_data['account_num']
        instance.account_amount = serializer.validated_data['account_amount']
        instance.save()
        # print(serializer.data)
        return Response({'code': 200, 'message': '修改成功', 'data': serializer.data})

    # 通过银行卡查询对应的记录(所有银行卡的记录)
    @action(detail=False, methods=['GET'])
    def record(self, request):
        # 请求方式 GET
        # 请求地址 /cost/account/detail/

        res = {'code': '8001', 'message': '错误请求', 'data': {'rule': '错误请求'}}
        raise ParamsException(res)
        # 把上面两句注销掉即可正常查询
        queryset = self.get_queryset()
        serializer = AccountDetailSerializer(queryset, many=True)
        return Response(serializer.data)

    # 通过银行卡id查找银行卡对应的消费信息
    @action(detail=False, methods=['GET', 'POST'])
    def records(self, request):
        # 请求方式 GET
        # 请求地址 /cost/account/[id]/detail/
        data = request.data
        # 取得用户登录id
        token = data.get('token')
        # 取得用户添加的银行卡id
        account_id = int(data.get('account'))
        # 判断card id是否属于该用户
        user = User.objects.get(pk=cache.get(token))
        account_id_list = [item.account_id for item in user.account_set.all()]
        if account_id not in account_id_list:
            res = {'code': '3003', 'message': '错误请求', 'data': {'rule': '错误请求'}}
            raise ParamsException(res)

        queryset = Account.objects.get(pk=account_id)
        serializer = AccountDetailSerializer(queryset)
        return Response(serializer.data)


# 交易详情,主要有新增和修改功能
class DetailView(viewsets.GenericViewSet,
                 mixins.CreateModelMixin,
                 mixins.ListModelMixin,
                 mixins.UpdateModelMixin):

    queryset = Details.objects.all()

    serializer_class = DetailSerializer

    filter_class = DetailFilter

    # 重写新增方法
    def create(self, request, *args, **kwargs):
        data = request.data
        # 取得时间
        detail_time = data.get('detail_time')
        # 取得用户登录id
        token = data.get('token')
        # 取得用户添加的银行卡id
        account_id = int(data.get('account'))
        # 判断card id是否属于该用户
        user = User.objects.get(pk=cache.get(token))
        account_id_list = [item.account_id for item in user.account_set.all()]
        if account_id not in account_id_list:
            res = {'code': '3003', 'message': '错误请求', 'data': {'rule': '错误请求'}}
            raise ParamsException(res)
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        account_id = serializer.validated_data['account']
        num = float(serializer.validated_data['detail_num'])
        detail_type = serializer.validated_data['detail_type']
        if detail_type not in ['收入', '借出', '餐饮', '通讯', '娱乐', '购物', '交通', '杂项']:
            res = {'code': '3002', 'message': '交易类型错误请求', 'data': {'rule': '错误请求'}}
            raise ParamsException(res)
        account_num = float(account_id.account_amount)
        if detail_type == '收入':
            account_num += num
        else:
            account_num -= num
        account_id.account_amount = account_num
        # 将记录填入本地，这儿有问题
        locals = Locals.objects.filter(Q(user_id=cache.get(token)) & Q(local_year=detail_time.split('-')[0].lstrip('0')) & Q(local_month=detail_time.split('-')[1].lstrip('0'))).first()
        # 如果没有记录但是请求数据正常，就创建新数据
        if not locals and (2024 >= int(detail_time.split('-')[0].lstrip('0')) >= 2019 and (12 >= int(detail_time.split('-')[1].lstrip('0')) >= 1)):
            locals = Locals()
            locals.local_year = detail_time.split('-')[0].lstrip('0')
            locals.local_month = detail_time.split('-')[1].lstrip('0')
            locals.user_id = cache.get(token)
            locals.save()
        elif not locals and (not(2024 >= int(detail_time.split('-')[0].lstrip('0')) >= 2019) or not(12 >= int(detail_time.split('-')[0].lstrip('0')) >= 1)):
            res = {'code': '3010', 'message': '错误请求,年份大于2019,小于2023', 'data': {'rule': '错误请求'}}
            raise ParamsException(res)

        if detail_type == '收入':
            locals.local_amount = float(locals.local_amount) + num
            locals.local_income = float(locals.local_income) + num
            locals.local_in = float(locals.local_in) + num
        elif detail_type == '借出':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_jie = float(locals.local_jie) + num
            locals.local_out = float(locals.local_out) + num
        elif detail_type == '餐饮':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_food = float(locals.local_food) + num
            locals.local_out = float(locals.local_out) + num
        elif detail_type == '通讯':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_phone = float(locals.local_phone) + num
            locals.local_out = float(locals.local_out) + num
        elif detail_type == '娱乐':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_game = float(locals.local_game) + num
            locals.local_out = float(locals.local_out) + num
        elif detail_type == '购物':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_shop = float(locals.local_shop) + num
            locals.local_out = float(locals.local_out) + num
        elif detail_type == '交通':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_traffic = float(locals.local_traffic) + num
            locals.local_out = float(locals.local_out) + num
        elif detail_type == '杂项':
            locals.local_amount = float(locals.local_amount) - num
            locals.local_other = float(locals.local_other) + num
            locals.local_out = float(locals.local_out) + num
        locals.save()
        account_id.save()
        serializer.save()
        return Response({'code': '200', 'message': '成功'})

    # 重写查询单条方法
    def retrieve(self, request, *args, **kwargs):
        # kwargs中的pk为默认的属性名，也就是路由后的/的内容
        # 如 127。0.0.1:8000/cost/1/ 下面则会获取到1对应的id对象
        instance = Details.objects.filter(pk=kwargs['pk']).first()
        instance = self.get_serializer(instance)
        return Response(instance.data)

    @action(detail=False, methods=['POST'])
    def show_one(self, request):
        detail_id = request.data.get('pk')
        instance = Details.objects.filter(pk=detail_id).first()
        instance = self.get_serializer(instance)
        return Response(instance.data)

    # 删除某条信息
    @action(detail=False, methods=['POST'])
    def delete_one(self, request):
        detail_id = request.data.get('pk')
        token = request.data.get('token')
        user = User.objects.get(pk=cache.get(token))
        details = Details.objects.filter(pk=detail_id).first()

        # 判断删除的余额信息是否属于该用户并且信息真实
        if not details or details.account.user.user_id != user.user_id:
            res = {'code': '3003', 'message': '错误请求', 'data': {'rule': '错误请求'}}
            raise ParamsException(res)
        times = details.detail_time
        # 判断删除方式
        delete_type = request.data.get('type')
        if delete_type == 'error':
            detail_num = details.detail_num
            detail_type = details.detail_type
            # 找到对象
            account = details.account
            if detail_type == '收入':
                account.account_amount = float(account.account_amount) - float(detail_num)
            else:
                account.account_amount = float(account.account_amount) + float(detail_num)
            # 还原数据

            locals = Locals.objects.filter(Q(user_id=cache.get(token)) & Q(local_year=times.year) & Q(local_month=times.month)).first()
            if not locals:
                res = {'code': '3007', 'message': '错误请求', 'data': {'rule': '没有对应的本地记录', 'year': times.year, 'month': times.month}}
                raise ParamsException(res)
            if detail_type == '收入':
                locals.local_amount = float(locals.local_amount) - float(detail_num)
                locals.local_income = float(locals.local_income) - float(detail_num)
                locals.local_in = float(locals.local_in) - float(detail_num)
            elif detail_type == '借出':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_jie = float(locals.local_jie) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)
            elif detail_type == '餐饮':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_food = float(locals.local_food) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)
            elif detail_type == '通讯':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_phone = float(locals.local_phone) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)
            elif detail_type == '娱乐':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_game = float(locals.local_game) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)
            elif detail_type == '购物':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_shop = float(locals.local_shop) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)
            elif detail_type == '交通':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_traffic = float(locals.local_traffic) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)
            elif detail_type == '杂项':
                locals.local_amount = float(locals.local_amount) + float(detail_num)
                locals.local_other = float(locals.local_other) - float(detail_num)
                locals.local_out = float(locals.local_out) - float(detail_num)

            locals.save()
            account.save()
        details.delete()
        return Response({'code': '200', 'message': '删除成功'})

    # 修改某条信息
    @action(detail=False, methods=['POST'], serializer_class=ChangeDetailSerializer)
    def change_one(self, request):
        detail_id = request.data.get('detail_id')
        token = request.data.get('token')
        user = User.objects.get(pk=cache.get(token))
        details = Details.objects.filter(pk=detail_id).first()
        # 判断修改的余额信息是否属于该用户并且信息真实
        if not details or details.account.user.user_id != user.user_id:
            res = {'code': '3003', 'message': '错误请求', 'data': {'rule': '记录不存在或不属于您'}}
            raise ParamsException(res)
        data = request.data
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': '200', 'message': '修改成功'})

# 检查版本
@api_view(['POST'])
def check_code(request):
    with open('./static/code/new_code.txt', 'r') as f:
        versionName = f.readline().strip('\n')
        versionCode = f.readline().strip('\n')
    return Response({'code': '200', 'message': '成功', 'data': {'versionName': versionName, 'versionCode': versionCode}})

# 检查版本
@api_view(['POST'])
def get_version_info(request):
    version_code = request.data.get('version_code')
    try:
        with open('./static/code/new_info_%s.txt' % version_code, 'r', encoding='utf-8') as f:
            version_info = f.read()
    except:
        res = {'code': '3003', 'message': '错误请求', 'data': {'rule': '错误请求'}}
        raise ParamsException(res)
    return Response({'code': '200', 'message': '成功', 'data': {'version_info': version_info}})


@api_view(['GET', 'POST'])
def show_local(request):
    year = request.data.get('year')
    month = request.data.get('month')
    token = request.data.get('token')
    user_id = cache.get(token)
    if year not in ['2019','2020', '2021', '2022', '2023', '2024'] or not year:
        res = {'code': '6001', 'message': '年份错误请求', 'data': {'rule': '最低年份2019，最高年份2024'}}
        raise ParamsException(res)
    if month not in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] or not month:
        res = {'code': '6002', 'message': '月份错误请求', 'data': {'rule': '最低月01，最高月12'}}
        raise ParamsException(res)
    # 判断记录是否存在
    locals = Locals.objects.filter(Q(local_year=year) & Q(local_month=month) & Q(user_id=cache.get(token))).first()
    if not locals:
        locals = Locals()
        locals.local_year = year
        locals.local_month = month
        locals.user_id = user_id
        locals.save()
        return Response(locals.to_dict())
    else:
        return Response(locals.to_dict())


@api_view(['GET', 'POST'])
def change_local(request):
    year = request.data.get('year')
    month = request.data.get('month')
    num = request.data.get('num')
    token = request.data.get('token')
    if year not in ['2020', '2021', '2022', '2023', '2024', '2019'] or not year:
        res = {'code': '6001', 'message': '年份错误请求', 'data': {'rule': '最低年份2019，最高年份2024'}}
        raise ParamsException(res)
    if month not in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] or not month:
        res = {'code': '6002', 'message': '月份错误请求', 'data': {'rule': '最低月01，最高月12'}}
        raise ParamsException(res)
    # 判断记录是否存在
    locals = Locals.objects.filter(Q(local_year=year) & Q(local_month=month) & Q(user_id=cache.get(token))).first()
    if not locals:
        res = {'code': '6003', 'message': '错误请求', 'data': {'rule': '没有这个记录'}}
        raise ParamsException(res)
    try:
        num = float(num)
    except:
        res = {'code': '6004', 'message': '错误请求', 'data': {'rule': '额度不合法'}}
        raise ParamsException(res)
    old_in = float(locals.local_in)
    old_out = float(locals.local_out)
    locals.local_amount = num + old_in - old_out
    locals.save()
    return Response({'code': '200', 'message': '修改成功', 'data': {}})


# 调用短信接口
@api_view(['GET', 'POST'])
def get_tel_code(request):
    tel = request.data.get('tel')
    if not re.fullmatch(r'1[3-9]\d{9}', tel):
        res = {'code': '7001', 'message': '错误请求', 'data': {'rule': '手机号不合法'}}
        raise ParamsException(res)
    re_type = request.data.get('type')
    # 找回密码
    user = User.objects.filter(tel=tel).first()
    if re_type == 'is_find':
        if not user:
            res = {'code': '7002', 'message': '该手机号尚未注册，不能找回密码', 'data': {'rule': '该手机号尚未注册，不能找回密码'}}
            raise ParamsException(res)
    if re_type == 'no_find':
        if user:
            res = {'code': '7003', 'message': '该手机号已注册，可找回密码', 'data': {'rule': '该手机号已注册，可找回密码'}}
            raise ParamsException(res)

    nums = ''.join([str(random.randint(0, 9)) for i in range(6)])
    client = AcsClient(ACCESSKEYID, ACCESSSECRET, 'cn-hangzhou')

    request = CommonRequest()
    request.set_accept_format('json')
    request.set_domain('dysmsapi.aliyuncs.com')
    request.set_method('POST')
    request.set_protocol_type('https')  # https | http
    request.set_version('2017-05-25')
    request.set_action_name('SendSms')

    request.add_query_param('RegionId', "cn-hangzhou")
    request.add_query_param('PhoneNumbers', tel)
    request.add_query_param('SignName', SIGN_NAME)
    request.add_query_param('TemplateCode', SMS_CODE)
    request.add_query_param('TemplateParam', "{\"code\":%s}" % nums)

    response = client.do_action(request)
    # python2:  print(response)
    if json.loads(str(response, encoding='utf-8'))['Code'] == 'OK':
        # 将验证码存入redis
        cache.set(tel, nums, timeout=300)
        return Response({'code': '200', 'message': '请求成功'})
    else:
        res = {'code': '7002', 'message': '发送失败,请检查手机号是否正确，或稍后再试', 'data': {'rule': '发送失败,请检查手机号是否正确，或稍后再试'}}
        raise ParamsException(res)


@api_view(['GET', 'POST'])
def show_weather(request):
    strs = eval(request.data.get('city'))
    for i in range(0, len(strs)):
        str1 = strs[i]
        nums = CITY_DICT.get(str1, 0)
        if nums == 0:
            continue
        else:
            return Response({'code': '200', 'message': nums})
    else:
        return Response({'code': '8000', 'message': '没有对应城市'})


@api_view(['GET'])
def get_code_img(request):

    tel = request.query_params.get('tel')
    if len(tel) != 11 or not re.fullmatch(TEL, tel):
        return Response()

    def random_color():
        """
        元组 （255，255，255）
        :return:
        """
        return (random.randint(110, 250), random.randint(110, 250), random.randint(110, 250))

    def text_random_color():
        """
        元组 （255，255，255）
        :return:
        """
        return (random.randint(50, 150), random.randint(50, 155), random.randint(50, 155))

    def random_char():
        """
        传入需生成字符的个数，返回列表
        :return:
        """
        temp = []
        for _ in range(4):
            a = chr(random.randint(65, 90))     # 生成大写字母
            b = chr(random.randint(97, 122))    # 生成小写字母
            c = str(random.randint(0, 9))       # 生成数字，注意要转换成字符串类型
            temp.append(random.choice([a, b, c]))
        return temp

    def create_lines():
        """
        干扰线
        :return:
        """
        size = (200, 40)
        for i in range(random.randint(2, 7)):
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw_obj.line((begin, end), fill=random_color(), width=random.randint(1, 4))

    def create_point():
        """
        干扰点
        :return:
        """
        for _ in range(40):
            draw_obj.point((random.randint(0, 220), random.randint(0, 40)), fill=random_color())
            x = random.randint(0, 220)
            y = random.randint(0, 40)
            z = random.randint(1, 7)
            a = random.randint(0, 270)
            b = random.randint(60, 360)
            draw_obj.arc((x, y, x+z, y+z), 0, 360, fill=random_color())
    # 生成一个图片对象
    img_obj = Image.new('RGB', (220, 40), random_color())
    #生成画笔对象
    draw_obj = ImageDraw.Draw(img_obj)
    # 生成字体对象
    font_obj = ImageFont.truetype('./static/font/ch.ttf', 40)
    # 将字符画到图片上
    char_list = random_char()
    # 转为小写
    char_list1 = ''.join([i.lower() for i in char_list])
    # 存入验证码至redis
    cache.set(tel[-4:], char_list1, timeout=30)

    for i in range(len(char_list)):
        draw_obj.text((10+50*i, 0), char_list[i], fill=text_random_color(), font=font_obj)
    create_lines()
    create_point()
    # img_obj.show()    # 调用默认图片浏览器查看

    # # 不需要在硬盘上保存文件，直接在内存中加载就可以
    io_obj = BytesIO()
    # 将生成的图片数据保存在io对象中
    img_obj.save(io_obj, "png")
    # 从io对象里面取上一步保存的数据
    data = io_obj.getvalue()

    response = HttpResponse(content_type='image/png')
    response['Content-Disposition'] = 'attachment; filename=test.png'
    response.write(data)
    return response


# echarts端
@api_view(('GET', 'POST'))
def get_local_map(request):
    year = request.data.get('year')
    month = request.data.get('month')
    token = request.data.get('token')
    times = '%s-%s' % (year, month)
    user_id = cache.get(token)
    local = Locals.objects.filter(Q(local_year=year) & Q(local_month=month) & Q(user_id=user_id)).first()
    data = []
    in_out = []
    if local:
        data.append({'value': local.local_income, 'name': '收入'})
        data.append({'value': local.local_jie, 'name': '借出'})
        data.append({'value': local.local_food, 'name': '餐饮'})
        data.append({'value': local.local_phone, 'name': '通讯'})
        data.append({'value': local.local_game, 'name': '娱乐'})
        data.append({'value': local.local_shop, 'name': '购物'})
        data.append({'value': local.local_traffic, 'name': '交通'})
        data.append({'value': local.local_other, 'name': '杂项'})
        in_out.extend([local.local_in, local.local_out])
    else:
        data.append({'value': 0, 'name': '收入'})
        data.append({'value': 0, 'name': '借出'})
        data.append({'value': 0, 'name': '餐饮'})
        data.append({'value': 0, 'name': '通讯'})
        data.append({'value': 0, 'name': '娱乐'})
        data.append({'value': 0, 'name': '购物'})
        data.append({'value': 0, 'name': '交通'})
        data.append({'value': 0, 'name': '杂项'})
    # 根据user_id取得银行卡
    accounts = Account.objects.filter(user_id=user_id).all()
    ids = [item.account_id for item in accounts]
    day = 31
    start_date = datetime.date(int(times.split('-')[0]), int(times.split('-')[1]), 1)
    # 算出结束时间
    while True:
        try:
            end_date = datetime.date(int(times.split('-')[0]), int(times.split('-')[1]), day)
            break
        except:
            day = day - 1
            continue
    # 找到所有记录
    details = Details.objects.filter(Q(account__in=ids) & Q(detail_time__range=(start_date, end_date))).all()
    # 折线图数据
    # 时间列表
    time_list = allDays(int(year), int(month))
    value_list = []
    for i in time_list:
        nums = 0
        for item in details:
            if item.detail_time.strftime('%Y-%m-%d').split('-')[-1] == i:
                nums += float(item.detail_num)
        value_list.append(nums)

    return Response({'code': 200, 'message': 'success', 'data':
        {'data': data, 'in_out': in_out, 'time_list': time_list, 'value_list': value_list}})


